package com.zhouyibike.uu.service;

import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.UUID;

import cn.easelive.lockencrypt.Encrypt;

/**
 * Service for managing connection and data communication with a GATT server hosted on a
 * given Bluetooth LE device.
 */
public class LockService extends Service implements Handler.Callback {
    private final static String TAG = LockService.class.getSimpleName();

    private BluetoothManager mBluetoothManager;
    private BluetoothAdapter mBluetoothAdapter;
    private String mBluetoothDeviceAddress;//连接的蓝牙设备编号
    private BluetoothGatt mBluetoothGatt;
    private BluetoothGattCharacteristic mTxChar;
    private int mConnectionState = STATE_DISCONNECTED;
    private int mConnectCount;
    private BleInfo mBleInfo = new BleInfo();
    private OnBleCallback mOnBleCallback;

    private final static int ACTION_GATT_CONNECTED = 0;
    private final static int ACTION_GATT_DISCONNECTED = 1;
    private final static int ACTION_GATT_READY = 2;
    private final static int ACTION_GATT_CHANGED = 3;

    private static final int STATE_CONNECTING = 1;
    private static final int STATE_DISCONNECTED = 0;
    private static final int STATE_CONNECTED = 2;

    private static byte[] INFO_CMD = {(byte) 0xE8, (byte) 0x01};
    private static byte[] OPEN_CMD = {(byte) 0xE8, (byte) 0x02};
    private static byte[] CLOSE_RECEIVED_CMD = {(byte) 0xE8, (byte) 0x83};

    public static final UUID RX_SERVICE_UUID = UUID.fromString("0000ff33-0000-1000-8000-00805f9b34fb");
    private static final UUID RX_CHAR_UUID = UUID.fromString("0000ff02-0000-1000-8000-00805f9b34fb");
    private static final UUID TX_CHAR_UUID = UUID.fromString("0000ff01-0000-1000-8000-00805f9b34fb");//写特征

    public static final int BLE_LINK_LOSS = 8;//搜索服务或者特征失败导致断开
    public static final int BLE_BLOCK = 133;//蓝牙阻塞

    private String custom_id;//客户id
    private Handler mHandler;

    @Override
    public boolean handleMessage(Message msg) {
        switch (msg.what) {
            case ACTION_GATT_CONNECTED:
                if (mOnBleCallback != null) {
                    mOnBleCallback.onConnected();
                }
                break;

            case ACTION_GATT_DISCONNECTED:
                if (mOnBleCallback != null) {
                    mOnBleCallback.onDisconnected();
                }
                break;

            case ACTION_GATT_READY:
                // TODO Auto-generated method stub
                mHandler.postDelayed(new Runnable() {
                    @Override
                    public void run() {
                        //查询设备状态
                        writeCommand(getStatusInfoCmd());
                    }
                }, 500);
                break;
            case ACTION_GATT_CHANGED:
                byte data[] = (byte[]) msg.obj;
                //  MLog.i(TAG, "收到数据：" + printHexString(data));
                if (data[0] == (byte) 0xE8) {
                    int result;
                    long lastPeriod;
                    ///    MLog.d(TAG, "received cmd:" + Integer.toHexString(data[1] & 0xFF));
                    switch (data[1]) {
                        case (byte) 0x81:
                            mBleInfo.hardwareVersion = data[2];
                            mBleInfo.lockStatus = data[3];
                            mBleInfo.batteryLevel = data[4];
                            mConnectCount = ((data[5] & 0xFF) << 8) | (data[6] & 0xFF);
                            mBleInfo.userId = String.valueOf(((data[7] & 0xFF) << 24) | ((data[8] & 0xFF) << 16) | ((data[9] & 0xFF) << 8) | (data[10] & 0xFF));
                            mBleInfo.lastPeriod = ((data[11] & 0xFF) << 24) | ((data[12] & 0xFF) << 16) | ((data[13] & 0xFF) << 8) | (data[14] & 0xFF);
                            mBleInfo.exceptionStatus = data[15];
                            if (mOnBleCallback != null) {
                                mOnBleCallback.onDeviceReady(mBleInfo);
                            }
                            //    MLog.i(TAG, "onReceive: 0x81 == " + mBleInfo.hardwareVersion + "\t" + mBleInfo.lockStatus + "\t" + mBleInfo.batteryLevel + "\t" +
                            //         mConnectCount + "\t" + mBleInfo.userId + "\t" + mBleInfo.lastPeriod + "\t" + mBleInfo.exceptionStatus);
                            break;
                        case (byte) 0x82:
                            result = (data[2] & 0xFF);
                            if (mOnBleCallback != null) {
                                mOnBleCallback.onOpenLock(result, getDataInfo(data, 3));
                            }
                            //   MLog.i(TAG, "onReceive: 0x82 == " + formatDate(getDataInfo(data, 3)));
                            break;
                        case (byte) 0x03:
                            writeCommand(CLOSE_RECEIVED_CMD);
                            result = (data[2] & 0xFF);

                            int platform = ((data[9] & 0xFF) << 24) | ((data[10] & 0xFF) << 16) | ((data[11] & 0xFF) << 8) | (data[12] & 0xFF);
                            lastPeriod = ((data[13] & 0xFF) << 24) | ((data[14] & 0xFF) << 16) | ((data[15] & 0xFF) << 8) | (data[16] & 0xFF);
                            if (mOnBleCallback != null) {
                                mOnBleCallback.onCloseLock(result, lastPeriod, getDataInfo(data, 3));
                            }
                            //     MLog.i(TAG, "onReceive: 0x03 == " + formatDate(getDataInfo(data, 3)) + " = platform = " + platform);
                            break;
                    }
                }
        }
        return false;
    }

    public class BleInfo {
        public int hardwareVersion; //版本号
        public int lockStatus;      //锁状态   0，1 0表示关，1表示开
        public int batteryLevel;    //电量    0~100   电池电量
        public String userId;      //用户ID，客户可以自行定义，一般用整数，高位在前
        public long lastPeriod;     //开锁时长      上一次的开锁持续时间
        public int exceptionStatus; //异常状态      未使用
    }

    //开锁蓝牙回调
    public interface OnBleCallback {
        void onConnected();

        void onDisconnected();

        void onDeviceReady(BleInfo info);

        void onOpenLock(int result, Date unlock);

        void onCloseLock(int result, long lastPeriod, Date lock);
    }

    // Implements callback methods for GATT events that the app cares about.  For example,
    // connection change and services discovered.
    //蓝牙gatt回调
    private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                mConnectionState = STATE_CONNECTED;
                //    MLog.i(TAG, "Connected to GATT server.");
                boolean is_start = mBluetoothGatt.discoverServices();
                //     MLog.i(TAG, "Attempting to start service discovery:" + is_start);

                sendMessage(ACTION_GATT_CONNECTED, null);
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                mConnectionState = STATE_DISCONNECTED;
                //    MLog.i(TAG, "Disconnected from GATT server.");

                disconnect();
                close();
                sendMessage(ACTION_GATT_DISCONNECTED, null);

                //非正常断开重连
                if (status == BLE_BLOCK || status == BLE_LINK_LOSS) {
                    connect(mBluetoothDeviceAddress);
                }
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                //    MLog.w(TAG, "mBluetoothGatt = " + gatt + " status=" + status);
                BluetoothGattService RxService = gatt.getService(RX_SERVICE_UUID);
                if (RxService == null) {
                    //        MLog.w(TAG, "Rx service not found!");
                    disconnect();
                    return;
                }
                mTxChar = RxService.getCharacteristic(TX_CHAR_UUID);
                if (mTxChar == null) {
                    //        MLog.w(TAG, "Tx charateristic not found!");
                    disconnect();
                    return;
                }
                BluetoothGattCharacteristic RxChar = RxService.getCharacteristic(RX_CHAR_UUID);
                if (RxChar == null) {
                    //        MLog.w(TAG, "Rx charateristic not found!");
                    disconnect();
                    return;
                }

                //如果对一个特性启用通知,当远程蓝牙设备特性发送变化，回调函数onCharacteristicChanged())被触发。
                gatt.setCharacteristicNotification(RxChar, true);
                for (BluetoothGattDescriptor dp : RxChar.getDescriptors()) {
                    dp.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                    gatt.writeDescriptor(dp);
                }

                sendMessage(ACTION_GATT_READY, null);
            } else {
                //     MLog.w(TAG, "onServicesDiscovered received: " + status);
                disconnect();
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt,
                                         BluetoothGattCharacteristic characteristic,
                                         int status) {
            //   MLog.d(TAG, "onCharacteristicRead:" + status);
            if (status == BluetoothGatt.GATT_SUCCESS) {

            }
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt,
                                          BluetoothGattCharacteristic characteristic,
                                          int status) {
            //   MLog.d(TAG, "onCharacteristicWrite:" + status);

        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt,
                                            BluetoothGattCharacteristic characteristic) {
            if (characteristic.getUuid().equals(RX_CHAR_UUID)) {
                sendMessage(ACTION_GATT_CHANGED, characteristic.getValue());
            }
        }
    };

    private void sendMessage(int what, Object object) {
        Message msg = new Message();
        msg.what = what;
        msg.obj = object;
        mHandler.sendMessage(msg);
    }

    public class LocalBinder extends Binder {
        public LockService getService() {
            return LockService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

    @Override
    public void onRebind(Intent intent) {
        super.onRebind(intent);
    }

    @Override
    public boolean onUnbind(Intent intent) {
        // After using a given device, you should make sure that BluetoothGatt.close() is called
        // such that resources are cleaned up properly.  In this particular example, close() is
        // invoked when the UI is disconnected from the Service.
        close();
        return true;
    }

    private final IBinder mBinder = new LocalBinder();

    /**
     * Initializes a reference to the local Bluetooth adapter.
     *
     * @return Return true if the initialization is successful.
     */
    public boolean initialize(OnBleCallback callback, String custom_id) {
        mHandler = new Handler(this);
        this.custom_id = custom_id;
        // For API level 18 and above, get a reference to BluetoothAdapter through
        // BluetoothManager.
        if (mBluetoothManager == null) {
            mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
            if (mBluetoothManager == null) {
                //      MLog.e(TAG, "Unable to initialize BluetoothManager.");
                return false;
            }
        }

        mBluetoothAdapter = mBluetoothManager.getAdapter();
        if (mBluetoothAdapter == null) {
            //   MLog.e(TAG, "Unable to obtain a BluetoothAdapter.");
            return false;
        }

        mOnBleCallback = callback;
        return true;
    }

    /**
     * Connects to the GATT server hosted on the Bluetooth LE device.
     *
     * @param address The device address of the destination device.
     * @return Return true if the connection is initiated successfully. The connection result
     * is reported asynchronously through the
     * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
     * callback.
     */
    //地址设备连接
    public boolean connect(final String address) {
        if (mBluetoothAdapter == null || address == null) {
            //   MLog.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
            return false;
        }

        if (mBluetoothGatt != null) {
            disconnect();
            close();
            //    MLog.i(TAG, "断开之前的连接");
        }
        //要连接的设备
        final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
        if (device == null) {
            //    MLog.w(TAG, "Device not found.  Unable to connect.");
            return false;
        }
        // We want to directly connect to the device, so we are setting the autoConnect
        // parameter to false.
        mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
        //   MLog.d(TAG, "Trying to create a new connection.");
        mBluetoothDeviceAddress = address;
        mConnectionState = STATE_CONNECTING;
        return true;
    }

    /**
     * Disconnects an existing connection or cancel a pending connection. The disconnection result
     * is reported asynchronously through the
     * {@code BluetoothGattCallback#onConnectionStateChange(android.bluetooth.BluetoothGatt, int, int)}
     * callback.
     */
    public void disconnect() {
        if (mBluetoothAdapter == null || mBluetoothGatt == null) {
            //  MLog.w(TAG, "BluetoothAdapter not initialized");
            return;
        }
        mBluetoothGatt.disconnect();
    }

    /**
     * After using a given BLE device, the app must call this method to ensure resources are
     * released properly.
     */
    private void close() {
        if (mBluetoothGatt == null) {
            return;
        }
        // MLog.w(TAG, "mBluetoothGatt closed");
        mBluetoothGatt.close();
        mBluetoothGatt = null;
    }

    public void openLock(String device_id, int user_id) {
//        if (mBleInfo.lockStatus == 0) {
        //   MLog.d(TAG, device_id + "---" + custom_id + "---" + mConnectCount);
        byte platform[] = new byte[6];  //开锁命令（2位） + 用户id（4位）
        platform[0] = (byte) 0xE8;
        platform[1] = (byte) 0x02;
        platform[2] = (byte) ((user_id >> 24) & 0xFF);
        platform[3] = (byte) ((user_id >> 16) & 0xFF);
        platform[4] = (byte) ((user_id >> 8) & 0xFF);
        platform[5] = (byte) ((user_id >> 0) & 0xFF);

        byte encrypt[] = Encrypt.Encode(device_id, custom_id, mConnectCount, platform);

        byte cmd[] = new byte[platform.length + encrypt.length];
        System.arraycopy(platform, 0, cmd, 0, platform.length);
        System.arraycopy(encrypt, 0, cmd, platform.length, encrypt.length);

        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < encrypt.length; i++) {
            builder.append(encrypt[i]).append(",");
        }
        //    MLog.i(TAG, "openLock: encrypt " + builder.toString());
        printLog("openLock: encrypt ", encrypt);

        builder.delete(0, builder.length() - 1);
        for (int i = 0; i < cmd.length; i++) {
            builder.append(cmd[i]).append(",");
        }
        //   MLog.i(TAG, "openLock cmd: " + builder.toString());
        printLog("openLock cmd: ", cmd);

        writeCommand(cmd);
//        }
    }

    private void writeCommand(byte[] value) {
        if (null != mBluetoothGatt && null != mTxChar) {
            mTxChar.setValue(value);
            for (int i = 0; i < 3; i++) {
                boolean status = mBluetoothGatt.writeCharacteristic(mTxChar);
                //    MLog.d(TAG, "write cmd:" + Integer.toHexString(value[1] & 0xFF) + " status=" + status);
                if (status) {
                    break;
                }
                /**
                 * 发送指令失败问题，主要是因为  mDeviceBusy = true,导致return
                 * 指令执行过程中未完成时，新的设备动作执行会导致阻塞
                 */
                try {
                    Thread.sleep(50);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 获取设备返回的时间
     *
     * @param data
     * @param start
     * @return
     */
    private Date getDataInfo(byte[] data, int start) {
        int year = 2 * 1000 + (data[start] & 0xFF);
        int month = (data[start + 1] & 0xFF);
        int day = (data[start + 2] & 0xFF);
        int hour = (data[start + 3] & 0xFF);
        int minute = (data[start + 4] & 0xFF);
        int second = (data[start + 5] & 0xFF);

        Date date = new Date(year - 1900, month - 1, day, hour, minute, second);

        return date;
    }

    private String formatDate(Date date) {
        SimpleDateFormat dateformat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss E");
        return dateformat.format(date);
    }

    private byte[] getStatusInfoCmd() {
        Calendar calendar = Calendar.getInstance();
        int year = calendar.get(Calendar.YEAR) - 2 * 1000;//年的后两位，即从2000年算起
        int month = calendar.get(Calendar.MONTH) + 1;
        int day = calendar.get(Calendar.DAY_OF_MONTH);
        int hour = calendar.get(Calendar.HOUR_OF_DAY);
        int minute = calendar.get(Calendar.MINUTE);
        int second = calendar.get(Calendar.SECOND);

        byte[] cmd = new byte[12];
        cmd[0] = (byte) 0xE8;
        cmd[1] = (byte) 0x01;
        cmd[2] = (byte) year;
        cmd[3] = (byte) month;
        cmd[4] = (byte) day;
        cmd[5] = (byte) hour;
        cmd[6] = (byte) minute;
        cmd[7] = (byte) second;

        byte[] platform = custom_id.getBytes();
        System.arraycopy(platform, 0, cmd, 8, platform.length);

        String log = "";
        for (int i = 0; i < cmd.length; i++) {
            log += (cmd[i] + "\t");
        }

        //   MLog.i(TAG, "getStatusInfoCmd: 状态命令" + log);

        return cmd;
    }

    private void printLog(String title, byte[] data) {
        StringBuilder builder = new StringBuilder();
        for (int i = 0; i < data.length; i++) {
            builder.append(String.format("02%x", data[i])).append(" , ");
        }
        //    MLog.i(TAG, title + printHexString(data));
    }

    public String printHexString(byte[] b) {
        String a = "";
        for (int i = 0; i < b.length; i++) {
            String hex = Integer.toHexString(b[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            a = a + "," + hex;
        }
        return a;
    }

    @Override
    public void onCreate() {
        // TODO Auto-generated method stub
        super.onCreate();
    }

    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        //  MLog.i(TAG, "onDestroy: ");
    }

}
